He creado un script de migración de matraz, sin embargo, cuando ejecuto la función de actualización, aparece el siguiente error:
INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.runtime.migration] Running upgrade -> 6378428b838a, empty message Traceback (most recent call last): File "migrate.py", line 22, in <module> manager.run() File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/flask_script/__init__.py", line 417, in run result = self.handle(argv[0], argv[1:]) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/flask_script/__init__.py", line 386, in handle res = handle(*args, **config) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/flask_script/commands.py", line 216, in __call__ return self.run(*args, **kwargs) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/flask_migrate/__init__.py", line 95, in wrapped f(*args, **kwargs) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/flask_migrate/__init__.py", line 280, in upgrade command.upgrade(config, revision, sql=sql, tag=tag) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/command.py", line 298, in upgrade script.run_env() File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/script/base.py", line 489, in run_env util.load_python_file(self.dir, "env.py") File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/util/pyfiles.py", line 98, in load_python_file module = load_module_py(module_id, path) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/util/compat.py", line 173, in load_module_py spec.loader.exec_module(module) File "<frozen importlib._bootstrap_external>", line 728, in exec_module File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed File "migrations/env.py", line 96, in <module> run_migrations_online() File "migrations/env.py", line 90, in run_migrations_online context.run_migrations() File "<string>", line 8, in run_migrations File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/runtime/environment.py", line 846, in run_migrations self.get_context().run_migrations(**kw) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/runtime/migration.py", line 518, in run_migrations step.migration_fn(**kw) File "/Users/slatifi/git/StaffTrainingLog/migrations/versions/6378428b838a_.py", line 23, in upgrade batch_op.create_foreign_key(None, 'organisation', ['organisation'], ['id']) File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/contextlib.py", line 119, in __exit__ next(self.gen) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/operations/base.py", line 325, in batch_alter_table impl.flush() File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/operations/batch.py", line 106, in flush fn(*arg, **kw) File "/Users/slatifi/git/StaffTrainingLog/venv/lib/python3.7/site-packages/alembic/operations/batch.py", line 390, in add_constraint raise ValueError("Constraint must have a name") ValueError: Constraint must have a nameHe visto a otras personas con el mismo error y simplemente agregaron render_as_batch al archivo env.py. Hice esto pero sigo teniendo el mismo error. ¿Alguna idea?
Nota: Esta es la modificación que hice en el archivo env.py:
with connectable.connect() as connection: context.configure( connection=connection, target_metadata=target_metadata, process_revision_directives=process_revision_directives, **current_app.extensions['migrate'].configure_args, render_as_batch=True )Este es el script de actualización creado por la migración.
from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '2838e3e96536' down_revision = None branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### with op.batch_alter_table('user', schema=None) as batch_op: batch_op.add_column(sa.Column('organisation', sa.String(length=5), nullable=False)) batch_op.create_foreign_key(None, 'organisation', ['organisation'], ['id']) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### with op.batch_alter_table('user', schema=None) as batch_op: batch_op.drop_constraint(None, type_='foreignkey') batch_op.drop_column('organisation') # ### end Alambique manda ###
Esto es normal porque SQLite3 no admite tablas ALTER.
Puede pasar render_as_batch=True durante la creación de instancias de Flask-Migrate de esta manera:
migrate = Migrate(app,db,render_as_batch=True)
No es necesario modificar el archivo env de Flask-Migrate.
Además, para evitar por completo el problema de sus futuras migraciones, puede crear plantillas de nombres de restricciones para todos los tipos de restricciones a los metadatos de SQLAlchemy, y luego creo que obtendrá nombres coherentes.
Vea cómo hacer esto en la documentación de Flask-SQLAlchemy . Estoy copiando el ejemplo de código de los documentos a continuación para su conveniencia:
from sqlalchemy import MetaData from flask import Flask from flask_sqlalchemy import SQLAlchemy convention = { "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", "ck": "ck_%(table_name)s_%(constraint_name)s", "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "pk": "pk_%(table_name)s" } metadata = MetaData(naming_convention=convention) db = SQLAlchemy(app, metadata=metadata)La instalación de Flask-Migrate ayuda a evitar las referencias de código a declarative_base o la modificación del archivo env.py Para las aplicaciones con fábrica de aplicaciones, divida la asignación de metadatos y el inicio de la __init__.py de datos entre los archivos extensions.py e db .
extensiones.py :
from sqlalchemy import MetaData from flask_sqlalchemy import SQLAlchemy metadata = MetaData( naming_convention={ "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", "ck": "ck_%(table_name)s_%(constraint_name)s", "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "pk": "pk_%(table_name)s" } ) db=SQLAlchemy(metadata=metadata)__init__.py :
from flask import Flask, Blueprint from config import ... from flask_migrate import Migrate from extensions import db ... def create_app(): app = Flask(__name__, template_folder='../app/templates') app.config.from_object(config[...]) config[...].init_app(app) db.init_app(app) with app.app_context(): db.create_all() migrate = Migrate(app, db) ... from .templates.main import main app.register_blueprint(main) return appLas otras respuestas muestran cómo configurar Flask para usar restricciones con nombre en el futuro , pero eso no resuelve el problema de eliminar las restricciones sin nombre existentes en las migraciones de Alambique. Para manejar cualquier restricción sin nombre existente, también debe definir la convención de nomenclatura en el script de migración, como se explica en los documentos de Alambique .
Por ejemplo, suponga que está intentando realizar una migración que agrega ON DELETE CASCADE a una clave externa existente en su tabla de test . Si siguió una de las otras respuestas (por ejemplo, https://stackoverflow.com/a/62651160/470844 ) y agregó la naming_convention de nombres al script de inicio de Flask, entonces la flask db upgrade de Flask generará algo como esto:
def upgrade(): with op.batch_alter_table('test', schema=None) as batch_op: batch_op.drop_constraint(None, type_='foreignkey') batch_op.create_foreign_key(batch_op.f('fk_test_user_id_user'), 'user', ['user_id'], ['id'], ondelete='CASCADE') Tenga en cuenta que mientras que la llamada create_foreign_key usa un nombre de restricción (es decir, fk_test_user_id_user ), la llamada drop_constraint aún usa None como nombre de restricción, lo que causará ValueError: Constraint must have a name en el título de esta pregunta.
Para solucionarlo, debe editar la migración para usar naming_convention y reemplazar None con el nombre de restricción generado. Por ejemplo, cambiaría la upgrade anterior a:
naming_convention = { "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", "ck": "ck_%(table_name)s_%(column_0_name)s", "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "pk": "pk_%(table_name)s" } def upgrade(): with op.batch_alter_table('test', schema=None, naming_convention=naming_convention) as batch_op: batch_op.drop_constraint('fk_test_user_id_user', type_='foreignkey') batch_op.create_foreign_key(batch_op.f('fk_test_user_id_user'), 'user', ['user_id'], ['id'], ondelete='CASCADE')(En este ejemplo, también necesitaría deshabilitar la compatibilidad con la clave externa de SQLite mientras ejecuta el script de migración, por ejemplo, utilizando la técnica aquí , para evitar que la migración por lotes active una eliminación en caché. El problema se documenta aquí ).